//*****************************************************************************
//
//  File........: RTC.c
//
//  Author(s)...: ATMEL Norway
//
//  Target(s)...: ATmega169
//
//  Compiler....: AVR-GCC 4.1.1; avr-libc 1.4.5
//
//  Description.: Real Time Clock (RTC)
//
//  Revisions...: 77
//
//  Modified last time by Hector Silva on 26/06/2013
//*****************************************************************************

//mtA
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "button.h"
//mtE
#include "main.h"
#include "RTC.h"
#include "LCD_functions.h"
#include "BCD.h"
#include "data_functions.h"
// mtA
//char gSECOND;
//char gMINUTE;
//char gHOUR;
//char gDAY;
//char gMONTH;
volatile uint8_t  gSECOND;
volatile uint8_t  gMINUTE;
volatile uint8_t  gHOUR;
volatile uint8_t  gDAY;

volatile uint8_t  gSECOND_INT_COUNT;
volatile uint8_t  gMINUTE_INT_COUNT;
volatile uint16_t  gHOUR_INT_COUNT;

volatile uint8_t  gSECOND_INT;
volatile uint8_t  gMINUTE_INT;
volatile uint16_t  gHOUR_INT;

extern volatile int16_t temperature;
//volatile uint8_t  gMONTH;
//volatile uint16_t gYEAR;

//char gPowerSaveTimer = 0;
//char dateformat = 0;
volatile uint8_t gPowerSaveTimer = 0;
uint8_t dateformat = 0;
// mtE
/*
// Lookup table holding the length of each mont. The first element is a dummy.
// mt this could be placed in progmem too, but the arrays are accessed quite
//    often - so leaving them in RAM is better...
char MonthLength[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

char TBL_CLOCK_12[] =   // table used when displaying 12H clock  
{12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

char clockformat = CLOCK_24;    // set initial clock format to 24H

// different date formates (text only)
// mtA
//__flash char EUROPEAN_DATE_TEXT[] =   "DDMMYY";
//__flash char AMERICAN_DATE_TEXT[] =   "MMDDYY";
//__flash char CANADIAN_DATE_TEXT[] =   "YYMMDD"; 
const char EUROPEAN_DATE_TEXT[] PROGMEM =   "DDMMYY";
const char AMERICAN_DATE_TEXT[] PROGMEM =   "MMDDYY";
const char CANADIAN_DATE_TEXT[] PROGMEM =   "YYMMDD"; 
// mtE

// different date formates, table for putting DD, MM and YY at the right place
// on the LCD
//mtA
//__flash char EUROPEAN_DATE_NR[] =   { 4, 5, 2, 3, 0, 1 };
//__flash char AMERICAN_DATE_NR[] =   { 4, 5, 0, 1, 2, 3 };
//__flash char CANADIAN_DATE_NR[] =   { 0, 1, 2, 3, 4, 5 }; 
const uint8_t EUROPEAN_DATE_NR[] PROGMEM =   { 4, 5, 2, 3, 0, 1 };
const uint8_t AMERICAN_DATE_NR[] PROGMEM =   { 4, 5, 0, 1, 2, 3 };
const uint8_t CANADIAN_DATE_NR[] PROGMEM =   { 0, 1, 2, 3, 4, 5 }; 
//mtE

//mtA
//__flash char __flash *DATEFORMAT_TEXT[] = {EUROPEAN_DATE_TEXT, AMERICAN_DATE_TEXT, CANADIAN_DATE_TEXT};
//__flash char __flash *DATE_FORMAT_NR[] = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};
PGM_P DATEFORMAT_TEXT[] = {EUROPEAN_DATE_TEXT, AMERICAN_DATE_TEXT, CANADIAN_DATE_TEXT};
const uint8_t *DATE_FORMAT_NR[] PROGMEM = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};
//mtE


*/

/******************************************************************************
*
*   Function name:  RTC_init
*
*   returns:        none
*
*   parameters:     none
*
*   Purpose:        Start Timer/Counter2 in asynchronous operation using a
*                   32.768kHz crystal.
*
*******************************************************************************/
void RTC_init(void)
{
    Delay(1000);            // wait for 1 sec to let the Xtal stabilize after a power-on,

    cli(); // mt __disable_interrupt();  // disabel global interrupt

    cbiBF(TIMSK2, TOIE2);             // disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        // select asynchronous operation of Timer2

    TCNT2 = 0;              // clear TCNT2A
    TCCR2A |= (1<<CS22) | (1<<CS20);             // select prescaler: 32.768 kHz / 128 = 1 sec between each overflow

    while((ASSR & 0x01) | (ASSR & 0x04));       // wait for TCN2UB and TCR2UB to be cleared

    TIFR2 = 0xFF;           // clear interrupt-flags
    sbiBF(TIMSK2, TOIE2);     // enable Timer2 overflow interrupt

    sei(); // mt __enable_interrupt();                 // enable global interrupt

    // initial time and date setting
    gSECOND  = 0;
    gMINUTE  = 0;
    gHOUR    = 0;
    // mt release timestamp
    gDAY     = 0;
	gSECOND_INT  = 0;
    gMINUTE_INT  = 5;
    gHOUR_INT    = 0;
	gSECOND_INT_COUNT  = 0;
    gMINUTE_INT_COUNT  = 0;
    gHOUR_INT_COUNT    = 0;
//    gMONTH   = 3;
//    gYEAR    = 9;
}


/*****************************************************************************
*
*   Function name : ShowClock
*
*   Returns :       char ST_state (to the state-machine)
*
*   Parameters :    char input (from joystick)
*
*   Purpose :       Shows the clock on the LCD
*
*****************************************************************************/
void ShowClock(void)
{
    //char HH, HL, MH, ML, SH, SL;
    uint8_t HH, HL, MH, ML, SH, SL;

    HH = CHAR2BCD2(gHOUR);
        
    HL = (HH & 0x0F) + '0';
    HH = (HH >> 4) + '0';

    MH = CHAR2BCD2(gMINUTE);
    ML = (MH & 0x0F) + '0';
    MH = (MH >> 4) + '0';

    SH = CHAR2BCD2(gSECOND);
    SL = (SH & 0x0F) + '0';
    SH = (SH >> 4) + '0';

    LCD_putc(0, HH);
    LCD_putc(1, HL);
    LCD_putc(2, MH);
    LCD_putc(3, ML);
    LCD_putc(4, SH);
    LCD_putc(5, SL);
    LCD_putc(6, '\0');

    LCD_Colon(1);

    LCD_UpdateRequired(TRUE, 0);
/*
    if (input == KEY_PREV);
        return ST_TIME_CLOCK;
    else if (input == KEY_NEXT);
        return ST_TIME_CLOCK_ADJUST;
      
    return ST_TIME_CLOCK_FUNC;
*/
}

void ShowTimeStamp(char days, char hours,char minutes)
{
    //char HH, HL, MH, ML, SH, SL;
    uint8_t HH, HL, MH, ML, SH, SL;

    HH = CHAR2BCD2(days);
        
    HL = (HH & 0x0F) + '0';
    HH = (HH >> 4) + '0';

    MH = CHAR2BCD2(hours);
    ML = (MH & 0x0F) + '0';
    MH = (MH >> 4) + '0';

    SH = CHAR2BCD2(minutes);
    SL = (SH & 0x0F) + '0';
    SH = (SH >> 4) + '0';

    LCD_putc(0, HH);
    LCD_putc(1, HL);
    LCD_putc(2, MH);
    LCD_putc(3, ML);
    LCD_putc(4, SH);
    LCD_putc(5, SL);
    LCD_putc(6, '\0');

    LCD_Colon(1);

    LCD_UpdateRequired(TRUE, 0);

}


#define HOUR       0
#define MINUTE     1
#define SECOND     2


/*****************************************************************************
*
*   Function name : SetClock
*
*   Returns :       char ST_state (to the state-machine)
*
*   Parameters :    char input (from joystick)
*
*   Purpose :       Adjusts the clock
*
*****************************************************************************/
void SetClock(char input)
{
    static char enter_function = 1;
    // mtA
    // static char time[3];    // table holding the temporary clock setting
    // static char mode = HOUR;
    // char HH, HL, MH, ML, SH, SL;
    static uint8_t time[3];
    static uint8_t mode = HOUR;
    uint8_t HH, HL, MH, ML, SH, SL;
    // mtE

    if (enter_function)
    {
        time[HOUR] = gHOUR;
        time[MINUTE] = gMINUTE;
        time[SECOND] = gSECOND;
    }

    HH = CHAR2BCD2(time[HOUR]);
        
    HL = (HH & 0x0F) + '0';
    HH = (HH >> 4) + '0';

    MH = CHAR2BCD2(time[MINUTE]);
    ML = (MH & 0x0F) + '0';
    MH = (MH >> 4) + '0';

    SH = CHAR2BCD2(time[SECOND]);
    SL = (SH & 0x0F) + '0';
    SH = (SH >> 4) + '0';

    LCD_putc(0, HH | ((mode == HOUR) ? 0x80 : 0x00));
    LCD_putc(1, HL | ((mode == HOUR) ? 0x80 : 0x00));
    LCD_putc(2, MH | ((mode == MINUTE) ? 0x80 : 0x00));
    LCD_putc(3, ML | ((mode == MINUTE) ? 0x80 : 0x00));
    LCD_putc(4, SH | ((mode == SECOND) ? 0x80 : 0x00));
    LCD_putc(5, SL | ((mode == SECOND) ? 0x80 : 0x00));
    LCD_putc(6, '\0');

    LCD_Colon(1);

    if (input != KEY_NULL)
        LCD_FlashReset();

    LCD_UpdateRequired(TRUE, 0);
    
    enter_function = 1;

    // Increment/decrement hours, minutes or seconds
    if (input == KEY_PLUS)
        time[mode]++;
    else if (input == KEY_MINUS)
        time[mode]--;
    else if (input == KEY_PREV)
    {
        if (mode == HOUR)
            mode = SECOND;
        else
            mode--;
    }
    else if (input == KEY_NEXT)
    {
        if (mode == SECOND)
            mode = HOUR;
        else
            mode++;
    }
    else if (input == KEY_ENTER)
    {
        // store the temporary adjusted values to the global variables
        cli(); // mt __disable_interrupt();
        gHOUR = time[HOUR];
        gMINUTE = time[MINUTE];
        gSECOND = time[SECOND];
        sei(); // mt __enable_interrupt();
        mode = HOUR;
//        return ST_TIME_CLOCK_FUNC;
    }

    /* OPTIMIZE: Can be solved by using a modulo operation */
    if (time[HOUR] == 255)
        time[HOUR] = 23;
    if (time[HOUR] > 23)
        time[HOUR] = 0;

    if (time[MINUTE] == 255)
        time[MINUTE] = 59;
    if (time[MINUTE] > 59)
        time[MINUTE] = 0;

    if (time[SECOND] == 255)
        time[SECOND] = 59;
    if (time[SECOND] > 59)
        time[SECOND] = 0;

    enter_function = 0;
//    return ST_TIME_CLOCK_ADJUST_FUNC;
}


/*****************************************************************************
*
*   Function name : SetInterval
*
*   Returns :       char ST_state (to the state-machine)
*
*   Parameters :    char input (from joystick)
*
*   Purpose :       Adjusts the interval
*
*****************************************************************************/
void SetInterval(char input)
{
    static char enter_function = 1;
    // mtA
    // static char time[3];    // table holding the temporary clock setting
    // static char mode = HOUR;
    // char HH, HL, MH, ML, SH, SL;
    static uint8_t time[3];
    static uint8_t mode = HOUR;
    uint8_t HH, HL, MH, ML, SH, SL;
    // mtE

    if (enter_function)
    {
        time[HOUR] = gHOUR_INT;
        time[MINUTE] = gMINUTE_INT;
        time[SECOND] = gSECOND_INT;
    }

    HH = CHAR2BCD2(time[HOUR]);
        
    HL = (HH & 0x0F) + '0';
    HH = (HH >> 4) + '0';

    MH = CHAR2BCD2(time[MINUTE]);
    ML = (MH & 0x0F) + '0';
    MH = (MH >> 4) + '0';

    SH = CHAR2BCD2(time[SECOND]);
    SL = (SH & 0x0F) + '0';
    SH = (SH >> 4) + '0';

    LCD_putc(0, ' '/* | ((mode == HOUR) ? 0x80 : 0x00)*/);
    LCD_putc(1, ' '/* | ((mode == HOUR) ? 0x80 : 0x00)*/);
    LCD_putc(2, HH | ((mode == HOUR) ? 0x80 : 0x00));
    LCD_putc(3, HL | ((mode == HOUR) ? 0x80 : 0x00));
    LCD_putc(4, MH | ((mode == MINUTE) ? 0x80 : 0x00));
    LCD_putc(5, ML | ((mode == MINUTE) ? 0x80 : 0x00));
    LCD_putc(6, '\0');

    LCD_Colon(1);

    if (input != KEY_NULL)
        LCD_FlashReset();

    LCD_UpdateRequired(TRUE, 0);
    
    enter_function = 1;

    // Increment/decrement hours, minutes or seconds
    if (input == KEY_PLUS){
		if(mode==MINUTE) time[mode]=time[mode]+5;
        else time[mode]++;}
    else if (input == KEY_MINUS){
        if(mode==MINUTE) time[mode]=time[mode]-5;
        else time[mode]--;}
    else if (input == KEY_PREV)
    {
        if (mode == HOUR)
            mode = MINUTE;
        else
            mode--;
    }
    else if (input == KEY_NEXT)
    {
        if (mode == MINUTE)
            mode = HOUR;
        else
            mode++;
    }
    else if (input == KEY_ENTER)
    {
		if(time[HOUR]==0 && time[MINUTE]==0) time[MINUTE]=5;
        // store the temporary adjusted values to the global variables
        cli(); // mt __disable_interrupt();
        gHOUR_INT = time[HOUR];
        gMINUTE_INT = time[MINUTE];
        gSECOND_INT = time[SECOND];
        sei(); // mt __enable_interrupt();
        mode = HOUR;
//        return ST_TIME_CLOCK_FUNC;
    }

    /* OPTIMIZE: Can be solved by using a modulo operation */
    if (time[HOUR] > 200)
        time[HOUR] = 24;
    if (time[HOUR] > 24)
        time[HOUR] = 0;

    if (time[MINUTE] > 200)
        time[MINUTE] = 55;
    if (time[MINUTE] > 59)
        time[MINUTE] = 0;

    if (time[SECOND] == 255)
        time[SECOND] = 59;
    if (time[SECOND] > 59)
        time[SECOND] = 0;

    enter_function = 0;
//    return ST_TIME_CLOCK_ADJUST_FUNC;
}


void check_for_interval(void){
	if(gMINUTE_INT==gMINUTE_INT_COUNT&&gHOUR_INT==gHOUR_INT_COUNT){
			cli();
			gSECOND_INT_COUNT=0;
			gMINUTE_INT_COUNT=0;
			gHOUR_INT_COUNT=0;
 			sei();
			//add here routine to record data and timestamp
			write_data(temperature);//temperature
			write_data(gMINUTE);//gMINUTE
			write_data(gHOUR);//gHOUR
			write_data(gDAY);//gDAY
			save_data_to_flash();


		}
}

#define YEAR        0
#define MONTH       1
#define DAY         2




/******************************************************************************
*
*   Timer/Counter2 Overflow Interrupt Routine
*
*   Purpose: Increment the real-time clock
*            The interrupt occurs once a second (running from the 32kHz crystal)
*
*******************************************************************************/
// mtA
// #pragma vector = TIMER2_OVF_vect
// __interrupt void TIMER2_OVF_interrupt(void)
// SIGNAL(SIG_OVERFLOW2)
ISR(TIMER2_OVF_vect)
// mtE
{
//    static char LeapMonth;

    gSECOND++;               // increment second
	gSECOND_INT_COUNT++;

    if (gSECOND == 60)
    {
        gSECOND = 0;
        gMINUTE++;
        gPowerSaveTimer++;
        
        if (gMINUTE > 59)
        {
            gMINUTE = 0;
            gHOUR++;

            if (gHOUR > 23)
            {
                
                gHOUR = 0;
                gDAY++;

               
            }
        }
    }
	if (gSECOND_INT_COUNT == 60)
    {
        gSECOND_INT_COUNT = 0;
		gMINUTE_INT_COUNT++;
        
        if (gMINUTE_INT_COUNT > 59)
        {
            gMINUTE_INT_COUNT = 0;
            gHOUR_INT_COUNT++;

        }
    }
}
